import os
import queue

from tool import mns_communication as mns_com, mns_time
from log import mns_log
import logging
from ship import ship_adj_table as ship_table

logger = mns_log.Log.performance_log()


class Router:

    def __init__(self, name, config: dict) -> None:
        self.table = RouterTable(name)
        self.name = name
        self.device_num = config["device_num"]
        self.send_round = config["send_round"]
        self.target = config["target"]
        self.message_size = config["message_size"]
        # 未准备好发送消息的队列
        self.not_ready_send_array = queue.Queue()
        # 待转发消息队列
        self.send_array = queue.Queue()
        # 待接收消息队列
        self.receive_array = queue.Queue()
        logging.info(
            "成功创建路由,当前路由协议为" + str(self.__class__.__name__) + ",连接设备数量为" + str(
                self.device_num) + ",设备发送信息周期为" + str(self.send_round))

    def send_message(self):
        """将要转发的消息放入队列"""
        not_ready_len = self.not_ready_send_array.qsize()
        for i in range(not_ready_len):
            msg = self.not_ready_send_array.get()
            self.handler_create_message(msg)
        for i in range(int(self.device_num)):
            if mns_time.Time.sim_time % int(self.send_round) == 0:
                self.create_message()

    def create_message(self):
        """创建消息"""
        if self.target == "random":
            target_host = ship_table.Table.choice_ship_by_random(self.name)
        else:
            # FIXME:非随机目标发送还未实现
            target_host = ""
        message = mns_com.Message(None, self.message_size, self.name, target_host)
        self.handler_create_message(message)

    def receive_message(self, message: mns_com.Message):
        """节点接收消息"""
        # 查看消息的目的节点是否是自身
        target_host = message.des
        if target_host == self.name:
            logger.info(self.name + "节点收到消息" + message.toString())
            return
        else:
            self.handler_create_message(message)

    def handler_create_message(self, msg: mns_com.Message):
        """将消息放入已就绪发送队列或未就绪发送队列"""
        target_host = msg.des
        # 获得中转节点
        transit_node = self.table.search_table(target_host)
        # 如果中转节点已在路由表中
        if transit_node:
            msg.tread_id = transit_node
            self.send_array.put(msg)
        # 如果中专节点未在路由表中
        else:
            msg.tread_id = None
            self.not_ready_send_array.put(msg)

    def update_router_table(self, neighbors_tables: dict):
        """
        更新当前路由的路由表
        :param neighbors_tables: 双层字典a{b:c} a为邻居节点名称，也就是从何处拿到的要合并的表，b为目的节点，c为路由条目
        :return:
        """
        self.table.combin_table(neighbors_tables)
        # logging.info(
        #     "当前仿真时间为" + str(
        #         mns_time.Time.sim_time) + "，设备" + self.name + "的路由表为" + self.table.toString())


class RouterTable:

    def __init__(self, name) -> None:
        self.name = name
        self.table = {}

    def create(self, des_host):
        """将领居船只的host放入路由表"""
        if des_host == self.name:
            return
        if self.table and (des_host in self.table.keys()):
            # 先判断是否已经存在
            des = self.table[des_host]
            if isinstance(des, RouterTableItem):
                # 如果已经存在
                des.update(des_host, 1)
                des.set_validate()
        else:
            item = RouterTableItem(des_host, des_host, 1)
            self.table[des_host] = item

    def insert(self, nei, des_host, item):
        """将别的host的路由表项插入"""
        if des_host == self.name:
            return
        if self.table and (des_host in self.table.keys()):
            des = self.table[des_host]
            if isinstance(des, RouterTableItem):
                _, _, num1, i1 = item.get()
                _, _, num2, i2 = des.get()
                # 如果要插入的路由条目的序列号值大于原有的则更新路由表
                if i1 > i2 and (not des.get_status() or num1 < num2):
                    # 如果要插入的item的跳数小于目前已有的item - 1
                    des.copy(nei, item)
                    des.set_validate()
        else:
            # 如果之前无该节点路由项
            # 增加总跳数
            _, d, num1, i1 = item.get()
            self.table[des_host] = RouterTableItem(nei, d, num1 + 1, i1)

    def search_table(self, des_host):
        """根据目的节点查找中转节点"""
        if self.table and des_host in self.table.keys():
            n_h, _, _, _ = self.table[des_host].get()
            return n_h

    # 合并两个路由表
    def combin_table(self, table: dict):
        """
        合并两个路由表
        :param table:双层字典a{b:c} a为邻居节点名称，也就是从何处拿到的要合并的表，b为目的节点，c为路由条目
        :return:
        """
        for key, value in table.items():
            self.create(key)
            if isinstance(value, RouterTable):
                for k, v in value.table.items():
                    self.insert(key, k, v)

    def toString(self):
        index = 1
        res = []
        res.append("i|  Next_hop  |  Des_node  |Hop_num|Serial")
        for key, value in self.table.items():
            res.append(value.toString(index))
            index += 1
        res_str = "\n"
        for r in res:
            res_str += r + "\n"
        return res_str


class RouterTableItem:
    def __init__(self, next_hop, des_node, hop_num: int, index: int = 0):
        self.next_hop = next_hop
        self.des_node = des_node
        self.hop_num = hop_num
        self.status = True
        # 路由条目序列号，如果同一终点的条目序列号小于自身序列号则废除，不予使用
        self.index = index

    # 使该条路由变为无效
    def set_invalidate(self):
        """使该条路由无效"""
        self.status = False

    # 使该条路由生效
    def set_validate(self):
        """使该条路由生效"""
        self.status = True

    # 判断该条路由的可用性
    def get_status(self):
        return self.status

    def get(self):
        return self.next_hop, self.des_node, self.hop_num, self.index

    def update(self, next_hop, hop_num):
        """更新路由表，自己发现的节点"""
        self.index += 1
        self.next_hop = next_hop
        self.hop_num = hop_num

    def copy(self, next_hop, item):
        """复制别的节点的路由条目"""
        self.next_hop = next_hop
        self.index = item.index
        self.hop_num = item.hop_num + 1

    def remove(self):
        del self

    def toString(self, index):
        return str(index) + "  " + self.next_hop + "  " + self.des_node + "    " + str(
            self.hop_num) + "       " + str(self.index)
